#include "datamodel.h"

#include <QFile>
#include <QTextStream>

#include <QMessageBox>

/*
	Model : The model keep the data in a list. Each tree widget node has an index
	wich allow to retrieve data from the model.

	The model also open and save file, and is in charge of modified flags.
	It is also responsible to switch the editor state to enabled/disabled.
*/
Model::Model(QTreeWidget *treeWidget, QTextEdit *editor, QString fileName)
{
    m_treeWidget = treeWidget;
    m_editor = editor;
    m_stFileName = fileName;

    m_treeWidget->clear();
    m_editor->clear();
    m_editor->setEnabled(false);
    m_boFileModified = true;

    m_folderIcon.addPixmap(treeWidget->style()->standardPixmap(QStyle::SP_DirClosedIcon),
                           QIcon::Normal, QIcon::Off);
    m_folderIcon.addPixmap(treeWidget->style()->standardPixmap(QStyle::SP_DirOpenIcon),
                           QIcon::Normal, QIcon::On);
    m_bookmarkIcon.addPixmap(treeWidget->style()->standardPixmap(QStyle::SP_FileIcon));

    list = new QList<ModelItem *>();
}

Model::~Model()
{
    ModelItem *item;
    
    foreach (item, *list)
    	delete item;
    
    delete list;        
}

void Model::clear()
{
    list->clear();
}

/*
	Add a new item in the model.
*/
int Model::addItem(QString name, QString type, QString data)
{
    int index;
    index = list->size();
    ModelItem *item = new ModelItem(name, type, data);
    list->insert(index, item);
    m_boFileModified = true;
    return index;
}

/*
	return an item name based on its index.
*/
QString Model::getName(int index)
{
    ModelItem *item = list->at(index);
    return item->itemName;
}

/*
	return an item type based on its index.
*/
QString Model::getType(int index)
{
    ModelItem *item = list->at(index);
    return item->itemType;
}

/*
	return an item data based on its index.
*/
QString Model::getData(int index)
{
    ModelItem *item = list->at(index);
    return item->itemData;
}

/*
	Update data on a given item, by its index.
*/
void Model::updateData(int index, QString data)
{
    ModelItem *item = list->at(index);
    item->itemData = data;
}

void Model::changeName(int index, QString newName)
{
    ModelItem *item = list->at(index);
    item->itemName = newName;
    m_boFileModified = true;
}

/*
	Save date into current filename.
*/
void Model::save()
{
    // Save the current page in the model, before saving.
    updateModelWithCurrent();
	
    writeFile();

    m_boFileModified = false;
}

/*
	Save data into a new filename.
*/
void Model::saveAs(QString fileName)
{
    m_stFileName = fileName;
    save();
}

/*
	Open the specified file.
*/
bool Model::open()
{
    // Clear model and tree widget.
    m_treeWidget->clear();
    clear();

    // Read the file.    
    bool result = readFile();

    m_currentItem = NULL;
    m_boFileModified = false;

    m_editor->document()->clear();
    m_editor->setEnabled(false);
    
    return result;
}



/*
	Update the model with new data from editor.
*/
void Model::updateModelWithCurrent()
{
    if ((m_currentItem != NULL) &&
            (m_editor->document()->isModified()))
    {
        int index = m_currentItem->data(0, Qt::UserRole).toUInt();
        updateData(index, m_editor->document()->toHtml());
        // The main document is modified...
        m_boFileModified = true;
        // ... but the current page is not, as the model has been updated.
        m_editor->document()->setModified(false);
    }
}

/*
	Check if the current document is modified.
*/
bool Model::checkFileModified()
{
    if (m_currentItem == NULL)
        return m_boFileModified;
    else
        return m_boFileModified || m_editor->document()->isModified();
}

/*
	Check if the current document is modified, propose to save it,
	and save it if asked.
*/
bool Model::askForSaveAndSave(QWidget *parent)
{
    if (checkFileModified())
    {
        QMessageBox::StandardButton ret;
        ret = QMessageBox::warning(parent, tr("Warning"),
                                   tr("The document has been modified.\n"
                                      "Do you want to save your changes?"),
                                   QMessageBox::Save | QMessageBox::Discard
                                   | QMessageBox::Cancel);
        if (ret == QMessageBox::Save)
        {
            if (m_stFileName != "")
            {
                save();
                return true;
            }
            else
                return true;
        }
        else if (ret == QMessageBox::Cancel)
            return false;
        else
            return true;
    }
    else
        return true;
}

/*
	Update editor with data from a new tree item.
*/
void Model::updateDisplay(QTreeWidgetItem *newItem)
{
    int index = newItem->data(0, Qt::UserRole).toUInt();
    QString type = getType(index);
    if (type == "PAGE")
    {
        // Save current page, before loading the new one.
        updateModelWithCurrent();

        QString data = getData(index);
        m_editor->document()->setHtml(data);
        m_editor->document()->setModified(false);
        m_editor->setEnabled(true);
        m_currentItem = newItem;
    }
}

/* Accessors */
QString Model::getFileName()
{
    return m_stFileName;
}

QTreeWidgetItem * Model::getCurrentItem()
{
    return m_currentItem;
}

void Model::setCurrentItem(QTreeWidgetItem *value)
{
    m_currentItem = value;
}

void Model::setFileModified(bool value)
{
    m_boFileModified = value;
}

/*
	ModelItem : internal class to store items.
*/
ModelItem::ModelItem(QString name, QString type, QString data)
{
    itemName = name;
    itemType = type;
    itemData = data;
}

/*
	Read/write methods
*/
void Model::ParseElement(const QDomElement &element, QTreeWidgetItem *parentItem, QTreeWidget *treeWidget)
{
	QString type;
    QString name;
    QString data;
    QString title;
    QString expanded;
    QTreeWidgetItem *childItem;

    QDomElement child = element.firstChildElement();
    while (!child.isNull())
    {
        name = child.tagName();
        if (name == "element")
        {
            type = child.attribute("type");
            if (type == "CATEGORY")
            {
            	// Get the name
                title = child.firstChildElement("name").text();
                
                expanded = child.attribute("expanded");
                
                // Create the tree item with appropriate parent.
                if (parentItem)
                {
                    childItem = new QTreeWidgetItem(parentItem);
                    childItem->setFlags(parentItem->flags());
                }
                else
                {
                	childItem = new QTreeWidgetItem(treeWidget);                	
               	}                    
                    
                if (expanded == "true")
                	childItem->setExpanded(true);
                else
                	childItem->setExpanded(false);

				// Setup icons and model.
                childItem->setIcon(0, this->m_folderIcon);
                childItem->setText(0, title);
                int index = this->addItem(title, "CATEGORY", "");
                childItem->setData(0, Qt::UserRole, index);

				// Category can have childs element, process them.
                ParseElement(child, childItem, treeWidget);
            }
            else if (type == "PAGE")
            {
            	// Get name and data.
                title = child.firstChildElement("name").text();
                data = child.firstChildElement("data").text();
                
                // Create the tree item with appropriate parent.
                if (parentItem)
                {
                    childItem = new QTreeWidgetItem(parentItem);
                    childItem->setFlags(parentItem->flags());
                }
                else
                {
                	childItem = new QTreeWidgetItem(treeWidget);
               	}                    
				
				// Setup icons and model.
                childItem->setIcon(0, this->m_bookmarkIcon);
                childItem->setText(0, title);
                int index = this->addItem(title, "PAGE", data);
                childItem->setData(0, Qt::UserRole, index);
            }
        }
        child = child.nextSiblingElement();
    }
}

/*
	Load the current file in the model.
*/
bool Model::readFile()
{
	QDomDocument m_doc;
	QFile file(m_stFileName);
	
    if (!file.open(QIODevice::ReadOnly))
        return false;
    if (!m_doc.setContent(&file))
    {
        file.close();
        return false;
    }
    file.close();
        
    QString type;
    QDomElement elem;
    QDomElement root = m_doc.documentElement();
    QDomElement node = root.toElement();

	// Loop on the nodes.
    while (!node.isNull())
    {
        ParseElement(node, NULL, m_treeWidget);
        node = node.nextSiblingElement();
    }
    return true;
}

/*
	Write the model using the tree structure.
*/
void Model::writeFile()
{
	QTextStream out;
	QDomDocument m_doc;
	QFile file;
	
	// Create the base element "document".
    QDomElement root = m_doc.createElement("document");
    m_doc.appendChild(root);
	
	// Setup the file
    file.setFileName(m_stFileName);
    if (!file.open(QIODevice::WriteOnly))
        return;        
    out.setDevice(&file);

	// Process the nodes.
    for (int i = 0; i < m_treeWidget->topLevelItemCount(); i++)
        processSaveItem(&m_doc, &root, m_treeWidget->topLevelItem(i));

    // Insert <?xml version="1.0" ?> at the begining of the document.
    QDomNode node = m_doc.createProcessingInstruction("xml","version=\"1.0\"");
    m_doc.insertBefore(node, m_doc.firstChild());
    // write the file.
    m_doc.save(out, 2);
    file.close();
}

/*
	Save Process nodes.
*/
void Model::processSaveItem(QDomDocument *doc, QDomElement *parent, QTreeWidgetItem *item)
{
    QString type;
    QString name;
    QString data;
    // Get index of data in the model, from the tree item.
    int index = item->data(0, Qt::UserRole).toUInt();
    // Get information from the model.
    type = this->getType(index);
    name = this->getName(index);
    data = this->getData(index);

    if (type == "CATEGORY")
    {
    	// Add an element node with its attibutes.
    	QDomElement element = doc->createElement("element");
		parent->appendChild(element);     
		element.setAttribute("type", "CATEGORY");
		if (item->isExpanded())
			element.setAttribute("expanded", "true");
		else
			element.setAttribute("expanded", "false");
		
		// Add the name node.
		QDomElement nameNode = doc->createElement("name");
		element.appendChild(nameNode);
		QDomText nameText = doc->createTextNode(name);
		nameNode.appendChild(nameText);     

		// A category can have sub-nodes : process them.
    	for (int i = 0; i < item->childCount(); i++)
	        processSaveItem(doc, &element, item->child(i));
    }
    else if (type == "PAGE")
    {
    	// Add an element node with its attibutes.
    	QDomElement element = doc->createElement("element");
		parent->appendChild(element);     
		element.setAttribute("type", "PAGE");
		
		// Add the name node.
		QDomElement nameNode = doc->createElement("name");
		element.appendChild(nameNode);
		QDomText nameText = doc->createTextNode(name);
		nameNode.appendChild(nameText);
		
		// Add the data node.
		QDomElement dataNode = doc->createElement("data");
		element.appendChild(dataNode);
		QDomCDATASection dataText = doc->createCDATASection(data);
		dataNode.appendChild(dataText);
    }
}

